package com.wang.user.service.user;

import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.RandomUtil;
import cn.hutool.core.util.ReUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.crypto.SecureUtil;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.wang.constant.*;
import com.wang.exception.BusinessException;
import com.wang.param.user.UserUpdateRequest;
import com.wang.param.weixin.RegisterThirdUserRequest;
import com.wang.param.weixin.WxBindingPhoneRequest;
import com.wang.reponse.*;
import com.wang.user.component.FrontTokenComponent;
import com.wang.user.component.RedisUtil;
import com.wang.user.mapper.UserMapper;
import com.wang.user.model.Evaluation;
import com.wang.user.model.Shop;
import com.wang.user.model.User;
import com.wang.user.model.UserToken;
import com.wang.user.service.EvaluationService;
import com.wang.user.service.PostCollectionService;
import com.wang.user.service.ShopCollectionService;
import com.wang.user.service.ShopService;
import com.wang.util.DateUtil;
import com.wang.util.WxUtil;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.codec.digest.DigestUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.support.TransactionTemplate;

import javax.annotation.Resource;
import java.util.Collections;
import java.util.List;
import java.util.UUID;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

/**
* @author wang3
* @description 针对表【user(用户主要信息)】的数据库操作Service实现
* @createDate 2024-11-10 23:54:14
*/
@Service("userServiceImpl")
@Slf4j
public class UserServiceImpl extends ServiceImpl<UserMapper, User>
    implements UserService{

    @Resource
    private UserMapper userMapper;

    @Resource
    private UserTokenService userTokenService;

    @Resource
    private WechatNewService wechatNewService;

    @Resource
    private FrontTokenComponent tokenComponent;

    @Resource
    private RedisUtil redisUtil;

    @Autowired
    private TransactionTemplate transactionTemplate;

    @Resource
    private ShopCollectionService shopCollectionService;

    @Resource
    private PostCollectionService postCollectionService;

    @Override
    public UserDetailVo getUserDetailWithFavorites(Integer uid) {
        // 获取用户基本信息
        User user = getById(uid);
        if (user == null) {
            throw new BusinessException("用户不存在");
        }

        UserDetailVo userDetailVo = new UserDetailVo();

        // 设置用户基本信息
        UserVo userVo = new UserVo();
        BeanUtils.copyProperties(user, userVo);
        userDetailVo.setUser(userVo);

        // 获取用户收藏的店铺
        List<ShopVo> shopVos = shopCollectionService.listShopCollectionByUid(uid);
        userDetailVo.setFavoriteShops(shopVos);

        // 获取用户收藏的评价
        List<PostVo> postVos = postCollectionService.listByUid(uid);
        userDetailVo.setFavoritePosts(postVos);

        return userDetailVo;
    }

    /**
     * 获取当前登录人信息
     * @return
     */
    @Override
    public User getLoginUser() {
        Integer id = tokenComponent.getUserId();
        if(ObjectUtil.isNull(id)){
            throw new BusinessException("用户未登录");
        }
        User user = getById(id);
        if(ObjectUtil.isNull(user)){
            throw new BusinessException("用户信息不存在");
        }
        return user;
    }

    /**
     * 根据手机号查询用户
     * @param phone 用户手机号
     * @return 用户信息
     */
    @Override
    public User getByPhone(String phone) {
        LambdaQueryWrapper<User> lqw = new LambdaQueryWrapper<>();
        lqw.eq(User::getPhone, phone);
        return getOne(lqw);
    }


    /**
     * 小程序登录
     * @param code String 前端临时授权code
     * @param request RegisterThirdUserRequest 用户信息
     * @return LoginResponse
     */
    @Override
    public LoginResponse weChatAuthorizeProgramLogin(String code, RegisterThirdUserRequest request) {
        WeChatMiniAuthorizeVo response = wechatNewService.miniAuthCode(code);
        System.out.println("小程序登陆成功 = " + JSON.toJSONString(response));

        //检测是否存在
        UserToken userToken = userTokenService.getByOpenidAndType(response.getOpenId());
        LoginResponse loginResponse = new LoginResponse();
        if (ObjectUtil.isNotNull(userToken)) {// 已存在，正常登录
            User user = getById(userToken.getUid());
            try {
                String token = tokenComponent.createToken(user);
                loginResponse.setToken(token);
            } catch (Exception e) {
                log.error(StrUtil.format("小程序登录生成token失败，uid={}", user.getUid()));
                e.printStackTrace();
            }
            loginResponse.setUid(user.getUid());
            loginResponse.setNikeName(user.getNickname());
            loginResponse.setPhone(user.getPhone());
            loginResponse.setOpenId(response.getOpenId());
            loginResponse.setSessionKey(response.getSessionKey());
            return loginResponse;
        }

        //用户不存在，注册一个
        User user = new User();
        user.setAccount(DigestUtils.md5Hex(getUuid() + DateUtil.getNowTime()));
        user.setNickname("微信账号" + RandomUtil.randomNumbers(5));
        String phoneAndAccount = "1761111" + RandomUtil.randomNumbers(4);
        user.setPhone(phoneAndAccount);
        user.setAccount(phoneAndAccount);
        user.setPwd("Abc" + RandomUtil.randomNumbers(5));
        user.setStatus(false); // 所有用户都是不可用的，可通过邀请加入家庭组之后使用
        try {
            save(user);
            userTokenService.bind(response.getOpenId(), user.getUid());
            String token = tokenComponent.createToken(user);
            loginResponse.setToken(token);
        } catch (Exception e) {
            throw new BusinessException("用户注册失败");
        }
        loginResponse.setUid(user.getUid());
        loginResponse.setNikeName(user.getNickname());
        loginResponse.setPhone(user.getPhone());
        loginResponse.setOpenId(response.getOpenId());
        loginResponse.setSessionKey(response.getSessionKey());
        return loginResponse;
    }


    /**
     * 微信注册绑定手机号
     * @param request 请求参数
     * @return 登录信息
     */
    @Override
    public LoginResponse registerBindingPhone(WxBindingPhoneRequest request) {
        checkBindingPhone(request);

        // 进入创建用户绑定手机号流程
        Object o = redisUtil.get(request.getKey());
        if (ObjectUtil.isNull(o)) {
            throw new BusinessException("用户缓存已过期，请清除缓存重新登录");
        }
        RegisterThirdUserRequest registerThirdUserRequest = JSONObject.parseObject(o.toString(), RegisterThirdUserRequest.class);

        boolean isNew = true; //是否是新用户
        User user = getByPhone(request.getPhone());
        if (ObjectUtil.isNull(user)) {
            user.setAccount(DigestUtils.md5Hex(getUuid() + DateUtil.getNowTime()));
            user.setNickname(registerThirdUserRequest.getNickName());
            user.setAvatar(registerThirdUserRequest.getAvatar());
            user.setPhone(request.getPhone());
            user.setAccount(request.getPhone());
            user.setPwd("Abc" + RandomUtil.randomNumbers(5));
            user.setStatus(false); // 所有用户都是不可用的，可通过邀请加入家庭组之后使用
        } else {// 已有账户，关联到之前得账户即可
            UserToken userToken = userTokenService.getTokenByUserId(user.getUid());
            if (ObjectUtil.isNotNull(userToken)) {
                throw new BusinessException("该手机号已被注册");
            }
            isNew = false;
        }

        boolean finalIsNew = isNew;
        Boolean execute = transactionTemplate.execute(e -> {
            if (finalIsNew){
                save(user);
            }
            userTokenService.bind(registerThirdUserRequest.getOpenId(), user.getUid());

            return Boolean.TRUE;
        });
        if (!execute) {
            log.error("微信用户注册生成失败，nickName = " + registerThirdUserRequest.getNickName());
        }
        LoginResponse loginResponse = new LoginResponse();
        try {
            String token = tokenComponent.createToken(user);
            loginResponse.setToken(token);
        } catch (Exception e) {
            log.error("绑定手机号，自动登录生成token失败，uid:{}, error:{}", user.getUid(),e.getMessage());
            throw new BusinessException("绑定手机号失败");
        }
        loginResponse.setUid(user.getUid());
        loginResponse.setNikeName(user.getNickname());
        loginResponse.setPhone(user.getPhone());
        return loginResponse;
    }

    @Override
    public List<User> getUserList() {
        return list();
    }

    @Override
    public User getUserDetail(Integer uid) {
        if(ObjectUtil.isNull(uid)){
            throw new BusinessException("用户id不能为空");
        }
        User user = getById(uid);
        if(ObjectUtil.isNull(user)){
            throw new BusinessException("用户信息不存在");
        }
        return user;
    }

    /**
     * 更新用户信息
     * @param request
     * @return
     */
    @Override
    public Boolean updateUser(UserUpdateRequest request) {
        try {
            if(ObjectUtil.isNull(request) || ObjectUtil.isNull(request.getUserName())){
                throw new BusinessException(ErrorCode.PARAMS_ERROR);
            }
            User loginUser = getLoginUser();
            loginUser.setNickname(request.getUserName());
            loginUser.setAvatar(request.getAvatarUrl());
            return updateById(loginUser);
        } catch (BusinessException e) {
            log.error("UserServiceImpl updateUser error: ",e);
            throw new BusinessException("更新用户信息失败");
        }
    }

    @Override
    public Boolean cancelUser(UserUpdateRequest request) {
        try {
            if(ObjectUtil.isNull(request)){
                throw new BusinessException(ErrorCode.PARAMS_ERROR);
            }
            User user = this.getById(request.getUid());
            if(ObjectUtil.isNull(user)){
                throw new BusinessException(ErrorCode.USER_NOT_EXIST);
            }
            user.setStatus(false);
            return updateById(user);
        } catch (BusinessException e) {
            log.error("UserServiceImpl cancelUser error: ",e);
            throw new BusinessException("注销用户失败");
        }
    }

    @Override
    public Boolean userRegister(String userAccount, String userPassword) {
        if (StringUtils.isAnyBlank(userAccount, userPassword)) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR);
        }
        if (userAccount.length() < 4) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR, "用户账号过短");
        }
        if (userPassword.length() < 5) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR, "用户密码过短");
        }
        synchronized ((Object) userAccount.hashCode()) {
            // 账户不能重复
            LambdaQueryWrapper<User> queryWrapper = new LambdaQueryWrapper<>();
            queryWrapper.eq(User::getAccount, userAccount);
            long count = this.baseMapper.selectCount(queryWrapper);
            if (count > 0) {
                throw new BusinessException(ErrorCode.PARAMS_ERROR, "账号重复");
            }
            // 2. 加密
//            String encryptPassword = DigestUtils.md5DigestAsHex((SALT + userPassword).getBytes());
            // 3. 插入数据
            User user = new User();
            user.setAccount(userAccount);
            // md5加密
            String md5Pwd = DigestUtils.md5Hex(userPassword);
            user.setPwd(md5Pwd);
            user.setUserType("0");
            user.setNickname(userAccount);
            boolean saveResult = this.save(user);
            if (!saveResult) {
                throw new BusinessException(ErrorCode.SYSTEM_ERROR, "注册失败，请重试");
            }
            return Boolean.TRUE;
        }
    }

    @Override
    public UserVo login(String userAccount, String userPassword) {
        if (StringUtils.isAnyBlank(userAccount, userPassword)) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR);
        }
        // 查询用户是否存在
        LambdaQueryWrapper<User> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(User::getAccount, userAccount);
        User tempUser = this.baseMapper.selectOne(queryWrapper);
        if(ObjectUtil.isNull(tempUser)){
            throw new BusinessException(ErrorCode.ACCOUNT_DOES_NOT_EXIST);
        }
        // md5加密后比对
        String md5Pwd = DigestUtils.md5Hex(userPassword);
        queryWrapper.eq(User::getPwd, md5Pwd);
        User user = this.baseMapper.selectOne(queryWrapper);
        // 用户不存在
        if (ObjectUtil.isNull(user)) {
            throw new BusinessException(ErrorCode.WRONG_PASSWORD);
        }

        String token = tokenComponent.createToken(user);
        UserVo userVo = new UserVo();
        BeanUtils.copyProperties(user, userVo);
        userVo.setToken(token);
        // 3. 记录用户的登录态 redis session
        return userVo;
    }


    /**
     * 绑定手机号数据校验
     */
    private void checkBindingPhone(WxBindingPhoneRequest request) {
        if (!request.getType().equals("public") && !request.getType().equals("routine") && !request.getType().equals("iosWx") && !request.getType().equals("androidWx")) {
            throw new BusinessException("未知的用户类型");
        }
        if (request.getType().equals("public") || request.getType().equals("iosWx") || request.getType().equals("androidWx")) {
            if (StrUtil.isBlank(request.getCaptcha())) {
                throw new BusinessException("验证码不能为空");
            }
            boolean matchPhone = ReUtil.isMatch(RegularConstants.PHONE_TWO, request.getPhone());
            if (!matchPhone) {
                throw new BusinessException("手机号格式错误，请输入正确得手机号");
            }
            // 公众号用户校验验证码
            boolean match = ReUtil.isMatch(RegularConstants.VALIDATE_CODE_NUM_SIX, request.getCaptcha());
            if (!match) {
                throw new BusinessException("验证码格式错误，验证码必须为6位数字");
            }
            checkValidateCode(request.getPhone(), request.getCaptcha());
        } else {
            // 参数校验
            if (StrUtil.isBlank(request.getCode())) {
                throw new BusinessException("小程序获取手机号code不能为空");
            }
            if (StrUtil.isBlank(request.getEncryptedData())) {
                throw new BusinessException("小程序获取手机号加密数据不能为空");
            }
            if (StrUtil.isBlank(request.getIv())) {
                throw new BusinessException("小程序获取手机号加密算法的初始向量不能为空");
            }
            // 获取appid
            String programAppId = AppConstant.AppId;
            if (StringUtils.isBlank(programAppId)) {
                throw new BusinessException("微信小程序appId未设置");
            }

            WeChatMiniAuthorizeVo response = wechatNewService.miniAuthCode(request.getCode());
//            WeChatMiniAuthorizeVo response = weChatService.programAuthorizeLogin(request.getCode());
            System.out.println("小程序登陆成功 = " + JSON.toJSONString(response));
            String decrypt = WxUtil.decrypt(programAppId, request.getEncryptedData(), response.getSessionKey(), request.getIv());
            if (StrUtil.isBlank(decrypt)) {
                throw new BusinessException("微信小程序获取手机号解密失败");
            }
            JSONObject jsonObject = JSONObject.parseObject(decrypt);
            if (StrUtil.isBlank(jsonObject.getString("phoneNumber"))) {
                throw new BusinessException("微信小程序获取手机号没有有效的手机号");
            }
            request.setPhone(jsonObject.getString("phoneNumber"));
        }
    }

    /**
     * 检测手机验证码
     * @param phone 手机号
     * @param code 验证码
     */
    private void checkValidateCode(String phone, String code) {
        Object validateCode = redisUtil.get(SmsConstants.SMS_VALIDATE_PHONE + phone);
        if (validateCode == null) {
            throw new BusinessException("验证码已过期");
        }
        if (!validateCode.toString().equals(code)) {
            throw new BusinessException("验证码错误");
        }
        //删除验证码
        redisUtil.delete(SmsConstants.SMS_VALIDATE_PHONE + phone);
    }

    private static String getUuid(){
        return UUID.randomUUID().toString().replace("-", "");
    }
}




